/*
 *  Copyright (C) 2020 original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License
 *  is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 *  or implied. See the License for the specific language governing permissions and limitations under
 *  the License.
 */

package vip.justlive.easyproxy.core.protocol;

import java.nio.ByteBuffer;
import vip.justlive.oxygen.core.exception.Exceptions;
import vip.justlive.oxygen.core.net.aio.core.AioHandler;
import vip.justlive.oxygen.core.net.aio.core.ChannelContext;

/**
 * 抽象代理处理类
 *
 * @author wubo
 */
public abstract class AbstractProxyDataHandler implements AioHandler {

  private static final ProxyData BEAT = new ProxyData().setType(ProxyDataType.BEAT);

  @Override
  public ByteBuffer encode(Object data, ChannelContext channelContext) {
    if (data instanceof ProxyData) {
      ProxyData proxyData = (ProxyData) data;
      int bodySize = 0;
      if (proxyData.getBody() != null) {
        bodySize = proxyData.getBody().length;
      }

      ByteBuffer buffer = ByteBuffer.allocate(ProxyData.BASE_LENGTH + bodySize);
      buffer.putInt(bodySize);
      buffer.putInt(proxyData.getId());
      buffer.putInt(proxyData.getType().getValue());
      if (bodySize > 0) {
        buffer.put(proxyData.getBody());
      }
      return buffer;
    }
    throw Exceptions
        .fail(String.format("type not match, %s -> %s", data.getClass(), ProxyData.class));
  }

  @Override
  public Object decode(ByteBuffer buffer, int readableSize, ChannelContext channelContext) {
    if (readableSize < ProxyData.BASE_LENGTH) {
      return null;
    }
    int bodySize = buffer.getInt();
    if (readableSize - ProxyData.BASE_LENGTH < bodySize) {
      return null;
    }
    ProxyData frame = new ProxyData().setId(buffer.getInt())
        .setType(ProxyDataType.from(buffer.getInt()));
    byte[] bytes = new byte[bodySize];
    buffer.get(bytes);
    return frame.setBody(bytes);
  }

  @Override
  public void handle(Object data, ChannelContext channelContext) {
    ProxyData proxyData = (ProxyData) data;
    if (proxyData.getType() == null || proxyData.getType() == ProxyDataType.BEAT) {
      return;
    }
    doHandle(proxyData, channelContext);
  }

  /**
   * 实际处理
   *
   * @param data 数据
   * @param channelContext channel上下文
   */
  public abstract void doHandle(ProxyData data, ChannelContext channelContext);

  @Override
  public Object beat(ChannelContext channelContext) {
    return BEAT;
  }
}
